package com.wang.tool.compile;


import com.wang.tool.util.CommonUtil;

import java.lang.ref.SoftReference;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Description 带缓存的编译器, 带锁线程安全
 * @see CacheableDynamicCompiler#getOrCompile
 * @Author shaopeng
 * @Date 2021/11/30
 * @Version 1.0
 */
public class CacheableDynamicCompiler {

    private static final JdkDynamicCompiler compiler = new JdkDynamicCompiler();

    private static final Map<String, SoftReference<Class<?>>> clsCache = new ConcurrentHashMap<>();

    /**
     * 线程安全
     *
     * @param cacheKey 缓存非空
     * @param code     代码
     * @return
     * @throws JdkDynamicCompiler.CompileException 编译失败/参数有问题
     */
    public static Class<?> getOrCompile(String cacheKey, String code) throws JdkDynamicCompiler.CompileException {
        if (CommonUtil.isBlank(cacheKey) || CommonUtil.isBlank(code)) {
            throw new JdkDynamicCompiler.CompileException("key or code can not be null");
        }
        SoftReference<Class<?>> clsReference = clsCache.get(cacheKey);
        Class<?> cls = clsReference == null ? null : clsReference.get();
        if (clsReference != null) {
            return cls;
        }
        synchronized (CacheableDynamicCompiler.class) {
            clsReference = clsCache.get(cacheKey);
            cls = clsReference == null ? null : clsReference.get();
            if (cls != null) {
                return cls;
            }
            cls = compiler.compile(code);
            if (cls == null) {
                throw new JdkDynamicCompiler.CompileException("compile result can not be null");
            }
            clsCache.put(cacheKey, new SoftReference<>(cls));
        }
        return cls;
    }

    /**
     * 清理类缓存
     */
    public void clear() {
        clsCache.clear();
    }


    public void remove(String key) {
        if (key != null) {
            clsCache.remove(key);
        }
    }


    public Class<?> get(String key) {
        if (key == null) {
            return null;
        }
        SoftReference<Class<?>> clsReference = clsCache.get(key);
        return clsReference == null ? null : clsReference.get();
    }
}
